Skip to content

feat(slots): check guest availability when host reschedules (#16378)#28911

Open
bcornish1797 wants to merge 3 commits intocalcom:mainfrom
bcornish1797:feat/16378-part-c-business-logic
Open

feat(slots): check guest availability when host reschedules (#16378)#28911
bcornish1797 wants to merge 3 commits intocalcom:mainfrom
bcornish1797:feat/16378-part-c-business-logic

Conversation

@bcornish1797
Copy link
Copy Markdown

@bcornish1797 bcornish1797 commented Apr 15, 2026

Third of three PRs splitting #28636. Depends on #28908 (Part A) and #28909 (Part B). This branch currently carries A+B+C so the test suite runs end to end; I'll rebase down to the Part C diff once A and B merge.

When the host reschedules a booking, the system now checks whether any attendee is a Cal.com user and fetches their busy times. Only mutually available slots are shown. If an attendee reschedules (rescheduledBy doesn't match the host email), all slots are shown, per @CarinaWolli's scope in #16378.

_getGuestBusyTimesForReschedule short-circuits on missing rescheduleUid or COLLECTIVE scheduling. Otherwise it loads the original booking, checks whether rescheduledBy matches the host email (case-insensitive), and fetches overlapping bookings for Cal.com-user attendees. Failures log at warn and don't block rescheduling. In getUserAvailability.ts, guestBusyTimes joins the EventBusyDetails list.

Tests in getGuestBusyTimesForReschedule.test.ts cover host vs attendee initiators, case-insensitive matching, backwards compat when rescheduledBy is absent, multi-guest merging, excludeUid, and every failure path.

/claim #16378 (bounty tracked on the original #28636)

Bcornish added 3 commits April 15, 2026 21:13
Split of calcom#28636 (Part B of 3). Plumbing only — adds a new `rescheduledBy`
search param that flows from the URL through `useScheduleForEvent` →
`useSchedule` → the tRPC `getSchedule` input schema. Nothing consumes
the field yet; it will be read by `_getGuestBusyTimesForReschedule` in
Part C to gate guest busy-time blocking on host-initiated reschedules.

Concretely:
- `apps/web/modules/schedules/hooks/useEvent.ts`: reads
  `searchParams.get("rescheduledBy")` alongside the existing
  `rescheduleUid` read and forwards it to `useSchedule`.
- `apps/web/modules/schedules/hooks/useSchedule.ts`: adds
  `rescheduledBy` to `UseScheduleWithCacheArgs` and passes it through
  to the tRPC query input.
- `packages/trpc/server/routers/viewer/slots/types.ts`: adds
  `rescheduledBy: z.string().nullish()` to `getScheduleSchemaObject`.

Backwards compatible — the field is nullish on both the URL and the
schema, so older clients keep working unchanged.
Split of calcom#28636 (Part A of 3). Pure additive infra — no call sites change
and no existing behaviour is altered. This layer is the data-access
foundation that Parts B (frontend `rescheduledBy` plumbing) and C
(slots/util.ts business logic) build on.

BookingRepository:
  - findByUidIncludeAttendeeEmails(uid): fetches an original booking's
    attendee emails and the host user's email, used to detect who the
    reschedule initiator is and resolve attendees to Cal.com users.
  - findByUserIdsAndDateRange({ userIds, userEmails, dateFrom, dateTo,
    excludeUid? }): finds ACCEPTED/PENDING bookings overlapping a date
    range by userId or attendee email (case-insensitive), with an
    excludeUid parameter applied at the database level so the caller
    cannot accidentally include the very booking being rescheduled.

UserRepository:
  - findByEmails({ emails }): resolves a list of emails to Cal.com
    users, checking both primary email and verified secondary emails,
    case-insensitively, with input deduplication before the query and
    output deduplication by user id. Uses Promise.all to fan out the two
    lookups concurrently.

Tests cover: empty-input short-circuits, primary vs secondary lookup,
dedup across both lookups, case-insensitive normalization, excludeUid,
OR clause composition, and the select shape used downstream.
…6378)

Split of calcom#28636 (Part C of 3 — depends on Parts A and B).

When the host reschedules a booking, check whether any attendee is a
Cal.com user and collect their busy times so the host only sees
mutually available slots. Attendee-initiated reschedules still see all
slots — guest-availability gating only kicks in when `rescheduledBy`
matches the host email.

Flow:

1. `slots/util.ts` reads `rescheduleUid` and `rescheduledBy` from the
   tRPC input (threaded through in Part B) and fans out
   `getGuestBusyTimesForReschedule` in the existing booking-fetch
   Promise.all.
2. `_getGuestBusyTimesForReschedule` (wrapped with `withReporting`):
    - short-circuits when no rescheduleUid or schedulingType is
      COLLECTIVE (team members already coordinated via round-robin);
    - loads the original booking with attendee + host email via
      `BookingRepository.findByUidIncludeAttendeeEmails` (Part A);
    - compares `rescheduledBy` to the host email (case-insensitive).
      If it is an attendee, returns `[]` — no blocking;
    - resolves attendee emails to Cal.com users via
      `UserRepository.findByEmails` (Part A) and filters the booking
      query to those users only, so non-Cal.com guests do not pollute
      the OR-filter;
    - fetches overlapping bookings via
      `BookingRepository.findByUserIdsAndDateRange` with excludeUid
      at the DB level (Part A);
    - on any failure, returns `[]` and logs a `warn` so operators
      can detect regressions without paging on a non-blocking path.
3. `getUserAvailability.ts` accepts the resulting `guestBusyTimes` via
   a new optional `initialData.guestBusyTimes` field, formats them as
   EventBusyDetails with a `guest-availability` source, and merges
   them into the per-user busy window. No existing call sites are
   changed — the field is optional and defaults to `[]`.

Scope:

- COLLECTIVE scheduling: skipped (already coordinated at booking time).
- Non-Cal.com guests: ignored (no Cal.com calendar to check).
- ROUND_ROBIN: fully supported.
- Multi-guest reschedules: all matched guests' busy times are merged.
- Older clients (no `rescheduledBy` in URL): fall through to gate on
  `rescheduleUid` alone. No regression in existing reschedule flows.

Tests (13 new, in
`packages/trpc/server/routers/viewer/slots/getGuestBusyTimesForReschedule.test.ts`):
early exits, host-initiated collection, attendee-initiated no-op,
case-insensitive host-email match, backwards compat when
rescheduledBy is absent, multi-guest merging, excludeUid filtering,
and graceful degradation on each failure mode.
@bcornish1797 bcornish1797 requested a review from a team as a code owner April 15, 2026 14:20
@github-actions
Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @bcornish1797! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 15cea3a17d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +685 to +689
if (rescheduledBy) {
const hostEmail = original.user?.email;
const isHostReschedule = hostEmail && rescheduledBy.toLowerCase() === hostEmail.toLowerCase();
if (!isHostReschedule) {
return [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard guest blocking when initiator is unknown

The host/attendee gate only runs inside if (rescheduledBy), so when rescheduledBy is missing this path defaults to applying guest busy-time blocking. That causes attendee-initiated reschedules from callers that still send only rescheduleUid (for example, apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots-input.service.ts maps bookingUidToReschedule but not rescheduledBy) to be treated like host reschedules, hiding slots attendees should still see.

Useful? React with 👍 / 👎.

Comment on lines +701 to +705
const calUserEmails = calUsers.map((u) => u.email);

const guestBookings = await this.dependencies.bookingRepo.findByUserIdsAndDateRange({
userIds: calUsers.map((u) => u.id),
userEmails: calUserEmails,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep matched attendee aliases in busy-time email filter

findByEmails resolves users by both primary and secondary emails, but here userEmails is rebuilt from calUsers.map((u) => u.email) (primary email only). If an attendee is matched via a verified secondary email, findByUserIdsAndDateRange will not match bookings where they attend under that secondary address, so guest busy windows are missed and conflicting slots can be offered.

Useful? React with 👍 / 👎.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 15, 2026

📝 Walkthrough

Walkthrough

This pull request adds support for computing guest attendee busy times during rescheduling operations. It introduces a rescheduledBy parameter to track who initiates a reschedule, which is threaded through scheduling hooks and API endpoints. Two new repository methods enable querying bookings and users by email. A new service method _getGuestBusyTimesForReschedule retrieves busy intervals for Cal.com user attendees in the target date range, excluding the rescheduled booking. The computed guest busy times are aggregated into the availability calculation. Changes include repository implementations, schema validation, service logic, and comprehensive test coverage across all new functionality.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature: checking guest availability when the host reschedules a booking. It is concise, specific, and directly reflects the primary objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description is directly related to the changeset. It explains the guest availability checking feature for host-initiated reschedules, how it works, and testing coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (4)
packages/features/users/repositories/UserRepository.test.ts (1)

130-130: Avoid double-casting the Prisma mock.

Line 130 uses as unknown as PrismaClient, which bypasses type-safety. UserRepository only uses four properties (user, membership, secondaryEmail, team) and two methods ($queryRaw, $transaction), so narrow the type to Pick<PrismaClient, "user" | "membership" | "secondaryEmail" | "team" | "$queryRaw" | "$transaction"> to enable type-safe mock injection without unsafe casting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/features/users/repositories/UserRepository.test.ts` at line 130,
Replace the unsafe double-cast of the Prisma mock (currently cast as "as unknown
as PrismaClient") with a narrow, type-safe Pick of PrismaClient: create the mock
using Pick<PrismaClient, "user" | "membership" | "secondaryEmail" | "team" |
"$queryRaw" | "$transaction"> and pass that to the UserRepository tests so
TypeScript enforces the four model properties and two methods used ($queryRaw,
$transaction) instead of bypassing type checks.
packages/trpc/server/routers/viewer/slots/util.ts (1)

802-817: Consider memoizing guest-busy lookups across fallback passes.

calculateHostsAndAvailabilities() is reused in the round-robin fallback flow, so doing the original-booking lookup + attendee resolution + overlap query here can repeat the same DB work 2–3 times in one request. Moving this up to _getAvailableSlots per date range, or memoizing by (rescheduleUid, rescheduledBy, schedulingType, dateFrom, dateTo), would keep the new logic off the hot path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/trpc/server/routers/viewer/slots/util.ts` around lines 802 - 817,
The guest-busy lookup (this.getGuestBusyTimesForReschedule) is being repeatedly
called when calculateHostsAndAvailabilities is used in the round-robin fallback
flow; memoize or hoist the lookup so it runs once per date-range/reschedule-key
and reuse it across fallback passes. Implement a small in-memory cache keyed by
the tuple (rescheduleUid, rescheduledBy, schedulingType, dateFrom.toISOString(),
dateTo.toISOString()) inside _getAvailableSlots (or higher) and check the cache
before calling getGuestBusyTimesForReschedule; ensure
calculateHostsAndAvailabilities accepts the precomputed guestBusyTimes (or reads
from the cache) so the DB/attendee-resolution work is not repeated.
packages/trpc/server/routers/viewer/slots/getGuestBusyTimesForReschedule.test.ts (2)

344-357: Assert the warn side of graceful degradation.

This only protects the [] fallback. If the catch stops emitting the warn, observability regresses and this test still passes. Please spy on the logger and assert the warning as part of this path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/trpc/server/routers/viewer/slots/getGuestBusyTimesForReschedule.test.ts`
around lines 344 - 357, The test currently only verifies the [] fallback but
doesn't assert that the logger.warn is called when the catch path is hit; update
the test in getGuestBusyTimesForReschedule.test.ts to spy on the process/logger
used by the implementation (e.g., spyOn(processLogger, 'warn') or the module
logger used by getGuestBusyTimesForReschedule) before invoking the function,
trigger the error path, call the function, and then assert the spy was called
with the expected warning message; finally restore/clear the spy to avoid test
leakage.

222-223: Drop these what-comments.

The test names and assertions already make this intent clear, so these comments just add noise.

As per coding guidelines, "Only add code comments that explain why, not what" and "Never add comments that simply restate what the code does".

Also applies to: 244-245, 266-267

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/trpc/server/routers/viewer/slots/getGuestBusyTimesForReschedule.test.ts`
around lines 222 - 223, Remove the redundant "what"-style inline comments in the
test file getGuestBusyTimesForReschedule.test.ts: delete the comments that
merely restate the test name/assertions (the ones flagged in the review and the
two additional occurrences) so each test relies on its descriptive test name and
assertions instead of duplicative commentary; ensure no functional code is
changed, only remove those comment lines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/modules/schedules/hooks/useSchedule.ts`:
- Line 107: The API V2 input type GetAvailableSlotsInput_2024_04_15 and the
/slots/available handler need to accept and use a rescheduledBy field to match
tRPC behavior: add an optional rescheduledBy:string to
GetAvailableSlotsInput_2024_04_15, surface it through the handler that powers
/slots/available, and implement the same host-reschedule detection logic
(case-insensitive compare against host email) used by the tRPC slots endpoint so
guest busy-times are only blocked when the reschedule was initiated by the host;
alternatively, if divergence is intentional, update the API docs/comments in the
/slots/available handler and GetAvailableSlotsInput_2024_04_15 to explicitly
state the behavioral difference.

In `@packages/features/availability/lib/getUserAvailability.ts`:
- Around line 621-626: Replace the hardcoded "Guest busy" string in the
guestBusyTimesFormatted mapping with a localization key lookup: add a new key
(e.g. "availability.guestBusy") to packages/i18n/locales/en/common.json and use
the i18n accessor used across the codebase (the project's translate/i18n
function) in getUserAvailability.ts when building EventBusyDetails (replace the
literal in guestBusyTimesFormatted with the i18n call), ensuring fallback to the
English string if translation is missing and keeping the rest of the object
shape unchanged.

In `@packages/features/bookings/repositories/BookingRepository.test.ts`:
- Around line 120-149: In the userId-only test case in
BookingRepository.test.ts, explicitly assert the userId predicate so the test
fails if filtering by userIds is omitted: after calling the repository method
under test (the "userId-only" scenario), iterate the returned bookings and
assert booking.userId === expectedUserId for each result (and/or assert the set
of returned userIds equals the single expectedUserId); this ensures the test not
only checks status/date overlap but also enforces that only bookings for the
specified user are returned.

---

Nitpick comments:
In `@packages/features/users/repositories/UserRepository.test.ts`:
- Line 130: Replace the unsafe double-cast of the Prisma mock (currently cast as
"as unknown as PrismaClient") with a narrow, type-safe Pick of PrismaClient:
create the mock using Pick<PrismaClient, "user" | "membership" |
"secondaryEmail" | "team" | "$queryRaw" | "$transaction"> and pass that to the
UserRepository tests so TypeScript enforces the four model properties and two
methods used ($queryRaw, $transaction) instead of bypassing type checks.

In
`@packages/trpc/server/routers/viewer/slots/getGuestBusyTimesForReschedule.test.ts`:
- Around line 344-357: The test currently only verifies the [] fallback but
doesn't assert that the logger.warn is called when the catch path is hit; update
the test in getGuestBusyTimesForReschedule.test.ts to spy on the process/logger
used by the implementation (e.g., spyOn(processLogger, 'warn') or the module
logger used by getGuestBusyTimesForReschedule) before invoking the function,
trigger the error path, call the function, and then assert the spy was called
with the expected warning message; finally restore/clear the spy to avoid test
leakage.
- Around line 222-223: Remove the redundant "what"-style inline comments in the
test file getGuestBusyTimesForReschedule.test.ts: delete the comments that
merely restate the test name/assertions (the ones flagged in the review and the
two additional occurrences) so each test relies on its descriptive test name and
assertions instead of duplicative commentary; ensure no functional code is
changed, only remove those comment lines.

In `@packages/trpc/server/routers/viewer/slots/util.ts`:
- Around line 802-817: The guest-busy lookup
(this.getGuestBusyTimesForReschedule) is being repeatedly called when
calculateHostsAndAvailabilities is used in the round-robin fallback flow;
memoize or hoist the lookup so it runs once per date-range/reschedule-key and
reuse it across fallback passes. Implement a small in-memory cache keyed by the
tuple (rescheduleUid, rescheduledBy, schedulingType, dateFrom.toISOString(),
dateTo.toISOString()) inside _getAvailableSlots (or higher) and check the cache
before calling getGuestBusyTimesForReschedule; ensure
calculateHostsAndAvailabilities accepts the precomputed guestBusyTimes (or reads
from the cache) so the DB/attendee-resolution work is not repeated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2f7462f5-faf8-4914-91b4-d790ab281d88

📥 Commits

Reviewing files that changed from the base of the PR and between 43ed117 and 15cea3a.

📒 Files selected for processing (10)
  • apps/web/modules/schedules/hooks/useEvent.ts
  • apps/web/modules/schedules/hooks/useSchedule.ts
  • packages/features/availability/lib/getUserAvailability.ts
  • packages/features/bookings/repositories/BookingRepository.test.ts
  • packages/features/bookings/repositories/BookingRepository.ts
  • packages/features/users/repositories/UserRepository.test.ts
  • packages/features/users/repositories/UserRepository.ts
  • packages/trpc/server/routers/viewer/slots/getGuestBusyTimesForReschedule.test.ts
  • packages/trpc/server/routers/viewer/slots/types.ts
  • packages/trpc/server/routers/viewer/slots/util.ts

timeZone: timezone ?? "PLACEHOLDER_TIMEZONE",
duration: duration ? `${duration}` : undefined,
rescheduleUid,
rescheduledBy,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Locate useApiV2AvailableSlots hook file"
HOOK_FILE="$(fd -i 'useApiV2AvailableSlots.ts' | head -n1 || true)"
if [ -z "${HOOK_FILE}" ]; then
  echo "useApiV2AvailableSlots.ts not found"
else
  echo "Found: ${HOOK_FILE}"
  echo
  echo "2) Inspect hook arg types and request serialization for rescheduledBy"
  rg -n -C4 --type=ts '\brescheduledBy\b|useApiV2AvailableSlots|available.?slots|query|params|body' "${HOOK_FILE}" || true
fi

echo
echo "3) Find server-side API V2 available-slots schema/handler references to rescheduledBy"
rg -n -C4 --type=ts '\brescheduledBy\b' apps packages | rg -n 'available.?slots|slots|v2|schema|router|handler' || true

Repository: calcom/cal.diy

Length of output: 45708


🏁 Script executed:

# Find the platform types definition for GetAvailableSlotsInput_2024_04_15
fd -i 'platform-types' --type d | head -5

Repository: calcom/cal.diy

Length of output: 40


🏁 Script executed:

# Search for GetAvailableSlotsInput_2024_04_15 definition
rg -n 'GetAvailableSlotsInput_2024_04_15' --type=ts -A 20 | head -100

Repository: calcom/cal.diy

Length of output: 10179


🏁 Script executed:

# Find the API V2 slots handler
fd -i '*slots*' -type f -path '*/api/v2/*' | grep -E '\.(ts|controller|handler)' | head -20

Repository: calcom/cal.diy

Length of output: 228


🏁 Script executed:

# Get complete GetAvailableSlotsInput_2024_04_15 type definition
cat packages/platform/types/slots/slots-2024-04-15/inputs/index.ts

Repository: calcom/cal.diy

Length of output: 6106


🏁 Script executed:

# Check how API V2 available slots service handles the input
rg -n 'rescheduledBy' apps/api/v2/src/lib/services/available-slots.service.ts --context=5

Repository: calcom/cal.diy

Length of output: 40


🏁 Script executed:

# Verify tRPC slots service actually uses rescheduledBy in implementation
rg -n 'rescheduledBy' packages/trpc/server/routers/viewer/slots/util.ts -B 2 -A 10 | head -60

Repository: calcom/cal.diy

Length of output: 1891


🏁 Script executed:

# Check if there's any other API V2 slots endpoint or service that might handle reschedule logic
rg -rn 'available.*slot' apps/api/v2/src/lib/services/ --type=ts | grep -i 'reschedule\|service'

Repository: calcom/cal.diy

Length of output: 40


API V2 /slots/available endpoint lacks rescheduledBy support, creating functional divergence with tRPC in host-reschedule guest-gating logic.

The API V2 GetAvailableSlotsInput_2024_04_15 type definition does not include a rescheduledBy field, so the endpoint cannot accept this parameter. Meanwhile, the tRPC slots endpoint uses rescheduledBy to determine whether guest busy-times should be included in available slots—only applying blocking when the reschedule is initiated by the host (matching emails case-insensitively). Without this parameter in API V2, the endpoint applies guest busy-time blocking uniformly, regardless of who initiated the reschedule.

To achieve parity, either:

  1. Add rescheduledBy to the API V2 input type and implement the same host-reschedule detection logic, or
  2. Accept that API V2 diverges intentionally and document the difference.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/modules/schedules/hooks/useSchedule.ts` at line 107, The API V2
input type GetAvailableSlotsInput_2024_04_15 and the /slots/available handler
need to accept and use a rescheduledBy field to match tRPC behavior: add an
optional rescheduledBy:string to GetAvailableSlotsInput_2024_04_15, surface it
through the handler that powers /slots/available, and implement the same
host-reschedule detection logic (case-insensitive compare against host email)
used by the tRPC slots endpoint so guest busy-times are only blocked when the
reschedule was initiated by the host; alternatively, if divergence is
intentional, update the API docs/comments in the /slots/available handler and
GetAvailableSlotsInput_2024_04_15 to explicitly state the behavioral difference.

Comment on lines +621 to +626
const guestBusyTimesFormatted: EventBusyDetails[] = (initialData?.guestBusyTimes ?? []).map((t) => ({
start: dayjs.utc(t.start).toISOString(),
end: dayjs.utc(t.end).toISOString(),
title: "Guest busy",
source: withSource ? "guest-availability" : "",
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace hardcoded "Guest busy" with an i18n key-backed string.
This introduces a user-facing English literal in a TypeScript path; please route it through localization resources instead of embedding raw text.

As per coding guidelines, "Add translations to packages/i18n/locales/en/common.json for all UI strings".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/features/availability/lib/getUserAvailability.ts` around lines 621 -
626, Replace the hardcoded "Guest busy" string in the guestBusyTimesFormatted
mapping with a localization key lookup: add a new key (e.g.
"availability.guestBusy") to packages/i18n/locales/en/common.json and use the
i18n accessor used across the codebase (the project's translate/i18n function)
in getUserAvailability.ts when building EventBusyDetails (replace the literal in
guestBusyTimesFormatted with the i18n call), ensuring fallback to the English
string if translation is missing and keeping the rest of the object shape
unchanged.

Comment on lines +120 to +149
it("should query bookings by userId when userIds are provided", async () => {
const mockBookings = [
{
uid: "booking-1",
startTime: new Date("2026-04-10T09:00:00Z"),
endTime: new Date("2026-04-10T10:00:00Z"),
title: "Meeting",
userId: 10,
status: BookingStatus.ACCEPTED,
},
];
mockPrismaClient.booking.findMany.mockResolvedValue(mockBookings);

const result = await repository.findByUserIdsAndDateRange({
userIds: [10],
userEmails: [],
dateFrom,
dateTo,
});

expect(result).toEqual(mockBookings);
expect(mockPrismaClient.booking.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({
status: { in: [BookingStatus.ACCEPTED, BookingStatus.PENDING] },
AND: [{ startTime: { lt: dateTo } }, { endTime: { gt: dateFrom } }],
}),
})
);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assert the userId predicate explicitly in the userId-only case.

Right now this test can pass even if the repository forgets to filter by userIds and only keeps status/date overlap.

Suggested test hardening
       const result = await repository.findByUserIdsAndDateRange({
         userIds: [10],
         userEmails: [],
         dateFrom,
         dateTo,
       });

       expect(result).toEqual(mockBookings);
-      expect(mockPrismaClient.booking.findMany).toHaveBeenCalledWith(
-        expect.objectContaining({
-          where: expect.objectContaining({
-            status: { in: [BookingStatus.ACCEPTED, BookingStatus.PENDING] },
-            AND: [{ startTime: { lt: dateTo } }, { endTime: { gt: dateFrom } }],
-          }),
-        })
-      );
+      const callArgs = mockPrismaClient.booking.findMany.mock.calls[0][0];
+      expect(callArgs.where).toEqual(
+        expect.objectContaining({
+          status: { in: [BookingStatus.ACCEPTED, BookingStatus.PENDING] },
+          AND: [{ startTime: { lt: dateTo } }, { endTime: { gt: dateFrom } }],
+        })
+      );
+      expect(JSON.stringify(callArgs.where)).toContain('"userId"');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/features/bookings/repositories/BookingRepository.test.ts` around
lines 120 - 149, In the userId-only test case in BookingRepository.test.ts,
explicitly assert the userId predicate so the test fails if filtering by userIds
is omitted: after calling the repository method under test (the "userId-only"
scenario), iterate the returned bookings and assert booking.userId ===
expectedUserId for each result (and/or assert the set of returned userIds equals
the single expectedUserId); this ensures the test not only checks status/date
overlap but also enforces that only bookings for the specified user are
returned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant